﻿using KqAutoDesign.Models;
using KqAutoDesign.Utilities;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web.Configuration;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using MessageBox = System.Windows.MessageBox;
using UserControl = System.Windows.Controls.UserControl;

namespace KqAutoDesign.SonViews
{
    /// <summary>
    /// WorkFlowEditUc.xaml 的交互逻辑
    /// </summary>
    public partial class WorkFlowEditUc : UserControl, INotifyPropertyChanged
    {
        public WorkFlowEditUc()
        {
            InitializeComponent();
            CanSave = false;
            DataContext = this;
        }
        public event Action WorkflowSaved;
        private WorkFlowBase _wb;
        public WorkFlowBase Wfb
        {
            get { return _wb; }
            set
            {
                if(_wb != value)
                {
                    _wb = value;
                    SelectedModule= null;
                    if(value==null)
                        CanSaveWf = false;
                    else
                        CanSaveWf=true;
                    RaisePropertyChanged(nameof(Wfb));
                }
            }
        }
        private ModuleBase _selectedModule;
        public ModuleBase SelectedModule 
        { 
            get{ return _selectedModule; }
            set
            {
                if (_selectedModule != value)
                {
                    _selectedModule = value;
                    RaisePropertyChanged(nameof(SelectedModule));
                    if(!string.IsNullOrEmpty(_script))
                    {
                        rtb.Document.Blocks.Clear();
                        _script = string.Empty;
                        pmLv.ItemsSource = null;
                    }
                } 
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void RaisePropertyChanged(string propName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propName));
            }
        }
        private Script _currentScript;
        public Script CurrentScript
        {
            get { return _currentScript; }
            set
            {
                if( _currentScript != value)
                {
                    _currentScript = value;
                    RaisePropertyChanged(nameof(CurrentScript));
                }
            }
        }
        private string _script;

        /// <summary>
        /// 替换运行脚本和参数按钮函数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tiHuan1Action(object sender, RoutedEventArgs e)
        {
            if(SelectedModule?.BatchScript != null)
            {
                var openFileDialog = new Microsoft.Win32.OpenFileDialog();
                openFileDialog.Filter = "批处理脚本|*.bat";
                var result = openFileDialog.ShowDialog();
                if (result == true)
                {
                    SelectedModule.BatchScript.OrgFilePath = openFileDialog.FileName;
                    Guid newGuid = Guid.NewGuid();
                    SelectedModule.BatchScript.FileName = newGuid.ToString() + Path.GetExtension(openFileDialog.FileName);
                    SelectedModule.BatchScript.Name = Path.GetFileNameWithoutExtension(openFileDialog.FileName);
                    if(CurrentScript!=null&&CurrentScript.Sctp==ScriptTypeEnum.BatchScript)
                    {
                        CurrentScript = null;
                        _script = string.Empty;
                        rtb.Document.Blocks.Clear();
                        pmLv.ItemsSource = null;
                    }
                }
            }
        }
        /// <summary>
        /// 编辑运行脚本和参数按钮函数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void edit1Action(object sender, RoutedEventArgs e)
        {
            if(SelectedModule?.BatchScript!=null)
            {
                if(!string.IsNullOrEmpty(_script))
                {
                    if (MessageBox.Show("是否放弃当前编辑内容？", "询问", MessageBoxButton.YesNo) == MessageBoxResult.No)
                        return;
                }
                CurrentScript = SelectedModule.BatchScript;
                string file = App.GlobalConfigs.WorkflowFolderPath + Wfb.Name + "\\" + SelectedModule.BatchScript.FileName;
                if (!File.Exists(file))
                {
                    MessageBox.Show("请先保存流程再编辑脚本。", "询问", MessageBoxButton.OK);
                    return;
                }
                if (LoadScript(file))
                    CanSave = false;
                else
                    CanSave = true;
            }
        }
        /// <summary>
        /// 加载脚本文件
        /// </summary>
        /// <param name="scriptFile"></param>
        public bool LoadScript(string scriptFile)
        {
            if (!File.Exists(scriptFile))
                return false;
            _script = File.ReadAllText(scriptFile);
            rtb.Document.Blocks.Clear();
            rtb.AppendText(_script);
            ScriptOperator opt = new ScriptOperator(_script);
            if(opt.GetAllParams()==false)
            {
                MessageBox.Show("脚本中的参数存在格式错误，请检查","错误",MessageBoxButton.OK, MessageBoxImage.Error);
                return false;
            }
            if (Check(CurrentScript.ScriptParams, opt.ScriptParams) == true)
            {
                pmLv.ItemsSource = CurrentScript.ScriptParams;
            }
            else
            {
                if (MessageBox.Show("保存参数与脚本中定义的参数不一致，是否重新定义参数？", "冲突", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
                {
                    CurrentScript.ScriptParams = ReGenerateParams();
                    pmLv.ItemsSource = CurrentScript.ScriptParams;
                }
            }

            foreach (var scp in opt.ScriptParams)
            {
                string content = "{[(" + scp.Content + ")]}";
                var trs = FindAllMatchedTextRanges(rtb, content);
                foreach (var tr in trs)
                {
                    tr.ApplyPropertyValue(ForegroundProperty, Brushes.Red);
                    tr.ApplyPropertyValue(FontWeightProperty, FontWeights.Bold);
                }
            }
            return true;
        }
        private List<TextRange> FindAllMatchedTextRanges(RichTextBox richBox, string keyword)
        {
            List<TextRange> trList = new List<TextRange>();
            //设置文字指针为Document初始位置
            TextPointer position = richBox.Document.ContentStart;
            while (position != null)
            {
                //向前搜索,需要内容为Text
                if (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
                {
                    //拿出Run的Text
                    string text = position.GetTextInRun(LogicalDirection.Forward);
                    //可能包含多个keyword,做遍历查找
                    int index = 0;
                    while (index < text.Length)
                    {
                        index = text.IndexOf(keyword, index);
                        if (index == -1)
                        {
                            break;
                        }
                        else
                        {
                            //添加为新的Range
                            TextPointer start = position.GetPositionAtOffset(index);
                            TextPointer end = start.GetPositionAtOffset(keyword.Length);

                            trList.Add(new TextRange(start, end));
                            index += keyword.Length;
                        }
                    }
                }
                //文字指针向前偏移
                position = position.GetNextContextPosition(LogicalDirection.Forward);
            }
            return trList;
        }
        /// <summary>
        /// 根据脚本中的参数标记，重新生成参数列表
        /// </summary>
        /// <returns>重新生成的参数列表</returns>
        private ObservableCollection<ScriptParam> ReGenerateParams()
        {
            ScriptOperator opt = new ScriptOperator(_script);
            if (opt.GetAllParams() == false)
            {
                return null;
            }
            ObservableCollection<ScriptParam> pms = new ObservableCollection<ScriptParam>();
            for (int i = 0; i < opt.ScriptParams.Count; i++)
            {
                ScriptParam scriptParam = new ScriptParam();
                scriptParam.ParamType = ParamTypeEnum.StringType;
                scriptParam.Id = Convert.ToInt32(opt.ScriptParams[i].Content);
                pms.Add(scriptParam);
            }
            return pms;
        }
        /// <summary>
        /// 校验脚本中的参数标记和右侧参数列表是否匹配
        /// </summary>
        /// <param name="pms">右侧参数列表</param>
        /// <param name="others">脚本中的标记</param>
        /// <returns>校验匹配返回true，否则返回false</returns>
        private bool Check(ObservableCollection<ScriptParam> pms, List<ParamData> others)
        {
            if (pms == null)
            {
                if (others==null||others.Count == 0)
                    return true;
                return false;
            }
            else
            {
                if (others.Count != pms.Count)
                    return false;
                foreach (var pm in pms)
                {
                    if (!others.Exists(x => x.Content == pm.Id.ToString()))
                        return false;
                }
                return true;
            }
        }


        /// <summary>
        /// 保存脚本
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void saveAction(object sender, RoutedEventArgs e)
        {
            if (pmLv.ItemsSource is ObservableCollection<ScriptParam> pms)
            {
                if(pms.Count(x=>string.IsNullOrEmpty(x.DefaultValue))>0)
                {
                    MessageBox.Show("属性默认值为必填，请补充！");
                    return;
                }
                var res = CheckParams(pms);
                if(res!=null)
                {
                    MessageBox.Show(res, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }
                if (MessageBox.Show("是否保存当前编辑内容？", "询问", MessageBoxButton.YesNo) == MessageBoxResult.No)
                    return;
                GlobalConfigXMLManager cfgManager = new GlobalConfigXMLManager();
                if(CurrentScript.Sctp== ScriptTypeEnum.BatchScript)
                    cfgManager.WriteBatchScriptParams(Wfb.Id, SelectedModule.Id, CurrentScript.Id, pms);
                else if(CurrentScript.Sctp == ScriptTypeEnum.FunctionScript)
                    cfgManager.WriteFunctionScriptParams(Wfb.Id, SelectedModule.Id, CurrentScript.Id, pms);
                string scriptFile = App.GlobalConfigs.WorkflowFolderPath + Wfb.Name + "\\" + CurrentScript.FileName;
                File.WriteAllText(scriptFile, _script);
            }
        }

        private string CheckParams(IList<ScriptParam> pms)
        {
            if(pms!=null)
            {
                foreach (ScriptParam p in pms)
                {
                    if(p.ParamType == ParamTypeEnum.BoolType)
                    {
                        if(p.DefaultValue.Equals("true",StringComparison.OrdinalIgnoreCase)||p.DefaultValue.Equals("false", StringComparison.OrdinalIgnoreCase))
                            continue;
                        return string.Format("参数{0}默认值与参数类型BoolType不匹配,请重新输入。",p.Id);
                    }
                    else if(p.ParamType == ParamTypeEnum.DoubleType)
                    {
                        try
                        {
                            Convert.ToDouble(p.DefaultValue);
                            continue;
                        }
                        catch (Exception)
                        {
                            return string.Format("参数{0}默认值与参数类型DoubleType不匹配,请重新输入。", p.Id);
                        }
                    }
                    else if (p.ParamType == ParamTypeEnum.IntType)
                    {
                        try
                        {
                            Convert.ToInt32(p.DefaultValue);
                            continue;
                        }
                        catch (Exception)
                        {
                            return string.Format("参数{0}默认值与参数类型IntType不匹配,请重新输入。", p.Id);
                        }
                    }
                }
                return null;
            }
            return null;
        }

        /// <summary>
        /// 编辑选中的功能脚本和参数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void edit2Action(object sender, RoutedEventArgs e)
        {
            if(cb.SelectedItem is Script fsc)
            {
                if (!string.IsNullOrEmpty(_script))
                {
                    if (MessageBox.Show("是否放弃当前编辑内容？", "询问", MessageBoxButton.YesNo) == MessageBoxResult.No)
                        return;
                }
                CurrentScript = fsc;
                string file = App.GlobalConfigs.WorkflowFolderPath + Wfb.Name + "\\" + CurrentScript.FileName;
                if (!File.Exists(file))
                {
                    MessageBox.Show("请先保存流程再编辑脚本。", "询问", MessageBoxButton.OK);
                    return;
                }
                if (LoadScript(file))
                    CanSave=false;
                else
                    CanSave=true;
            }
        }

        private void getParamAction(object sender, RoutedEventArgs e)
        {
            TextRange textRange = new TextRange(rtb.Document.ContentStart, rtb.Document.ContentEnd);
            _script = textRange.Text;
            if (SelectedModule == null || string.IsNullOrEmpty(_script)||CurrentScript==null)
                return;
            ScriptOperator opt = new ScriptOperator(_script);
            if (opt.GetAllParams() == false)
            {
                MessageBox.Show("脚本中的参数存在格式错误，请检查", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }
            //对比两个列表，把右侧存在，但左侧不存在的项删除
            var notExist = CurrentScript?.ScriptParams?.Where(x=>!opt.ScriptParams.Exists(p=>p.Content== x.Id.ToString())).ToList();
            notExist?.ForEach(x => CurrentScript.ScriptParams.Remove(x));
            //把左侧存在，但右侧不存在的项增加
            var exists = opt.ScriptParams?.Where(x => CurrentScript.ScriptParams.FirstOrDefault(p => p.Id.ToString() == x.Content) == null).ToList();
            exists.ForEach(x => CurrentScript.ScriptParams.Add(new ScriptParam
            {
                Id = Convert.ToInt32(x.Content),
                Name = "参数" + x.Content,
                ParamType = ParamTypeEnum.DoubleType
            }));
            pmLv.ItemsSource = CurrentScript.ScriptParams;
            CanSave = true;
        }

        private bool _canSave;
        public bool CanSave
        {
            get { return _canSave; }
            set
            {
                if(_canSave != value)
                {
                    _canSave = value;
                    RaisePropertyChanged(nameof(CanSave));
                }
            }
        }

        private bool _canSaveWf;
        public bool CanSaveWf
        {
            get { return _canSaveWf; }
            set
            {
                if (_canSaveWf != value)
                {
                    _canSaveWf = value;
                    RaisePropertyChanged(nameof(CanSaveWf));
                }
            }
        }

        private void rtbTextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            CanSave = false;
        }

        /// <summary>
        /// 增加模块按钮功能
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void addModuleAction(object sender, RoutedEventArgs e)
        {
            int id = 1;
            if (Wfb == null)
                return;
            if(Wfb.Modules?.Count>0)
            {
                id = Wfb.Modules.Max(x => x.Id) + 1;
            }
            NewModule newModuleDialog = new NewModule(id);
            if(newModuleDialog.ShowDialog()==true)
            {
                Wfb.Modules.Add(newModuleDialog.Module);
            }
        }

        private void moveUpAction(object sender, RoutedEventArgs e)
        {
            if (Wfb==null||Wfb.Modules?.Count <= 1)
                return;
            int id = Wfb.Modules.IndexOf(SelectedModule);
            if (SelectedModule != null&& id>0)
            {
                var mdCloned = SelectedModule.Clone();
                Wfb.Modules.RemoveAt(id);
                Wfb.Modules.Insert(id-1, mdCloned);
                for (int i = 0; i < Wfb.Modules.Count; i++)
                {
                    Wfb.Modules[i].Id = i + 1;
                }
            }
        }

        private void MoveDownAction(object sender, RoutedEventArgs e)
        {
            if (Wfb == null || Wfb?.Modules?.Count <= 1)
                return;
            int id = Wfb.Modules.IndexOf(SelectedModule);
            if (SelectedModule != null && id >= 0)
            {
                var mdCloned = SelectedModule.Clone();
                Wfb.Modules.RemoveAt(id);
                Wfb.Modules.Insert(id + 1, mdCloned);
                for (int i = 0; i < Wfb.Modules.Count; i++)
                {
                    Wfb.Modules[i].Id = i + 1;
                }
            }
        }

        private void DelModuleAction(object sender, RoutedEventArgs e)
        {
            if(SelectedModule!=null)
            {
                int id = Wfb.Modules.IndexOf(SelectedModule);
                Wfb?.Modules.RemoveAt(id);
                for (int i = 0; i < Wfb.Modules.Count; i++)
                {
                    Wfb.Modules[i].Id = i + 1;
                }
            }
        }

        private void SaveFlowAction(object sender, RoutedEventArgs e)
        {
            WorkflowSaved?.Invoke();
        }

        private void SubstituteAction(object sender, RoutedEventArgs e)
        {
            if (SelectedModule?.FunctionScripts != null&&CurrentScript!=null&&CurrentScript.Sctp==ScriptTypeEnum.FunctionScript)
            {
                var openFileDialog = new Microsoft.Win32.OpenFileDialog();
                openFileDialog.Filter = "功能脚本|*.*";
                var result = openFileDialog.ShowDialog();
                if (result == true)
                {
                    var index = SelectedModule.FunctionScripts.IndexOf(CurrentScript);
                    Script script = new Script();
                    script.Id = CurrentScript.Id;
                    script.OrgFilePath = openFileDialog.FileName;
                    script.Sctp = ScriptTypeEnum.FunctionScript;
                    Guid newGuid = Guid.NewGuid();
                    script.FileName = newGuid.ToString() + Path.GetExtension(openFileDialog.FileName);
                    script.Name = Path.GetFileNameWithoutExtension(openFileDialog.FileName);
                    SelectedModule.FunctionScripts.RemoveAt(index);
                    SelectedModule.FunctionScripts.Insert(index, script);
                    CurrentScript = script;
                    _script = string.Empty;
                    rtb.Document.Blocks.Clear();
                    pmLv.ItemsSource = null;
                }
            }
        }

        private void OpenDicAction(object sender, RoutedEventArgs e)
        {
            if (sender is Button btn && btn.DataContext is ScriptParam sp)
            {
                ParamDictionaryWd wd = new ParamDictionaryWd();
                if (wd.ShowDialog() == true)
                {
                    sp.Value = wd.Gsp?.ScriptParam.Value;
                    sp.DefaultValue = wd.Gsp?.ScriptParam.Value;
                    sp.Description = wd.Gsp?.ScriptParam.Description;
                }
            }
        }

        private void SetParamMenuItemAction(object sender, RoutedEventArgs e)
        {
            TextRange textRange = new TextRange(rtb.Document.ContentStart,rtb.Document.ContentEnd);
            ScriptOperator opt = new ScriptOperator(textRange.Text);
            opt.GetAllParams();
            if (opt.ScriptParams == null || opt.ScriptParams.Count==0)
                rtb.Selection.Text = "{[(0)]}";
            else
            {
                int nextId = opt.ScriptParams.Max(x => Convert.ToInt32(x.Content)) + 1;
                rtb.Selection.Text = "{[(" + nextId.ToString() + ")]}";
            }
        }

        private void ContextMenu_ContextMenuOpening(object sender, ContextMenuEventArgs e)
        {
            RichTextBox rtb = sender as RichTextBox;
            ContextMenu cm = rtb.ContextMenu;
            foreach (MenuItem mi in cm.Items)
            {
                if ((String)mi.Name == "SetParam")
                {
                    if(!string.IsNullOrEmpty(rtb.Selection.Text))
                        mi.IsEnabled = true;
                    else
                        mi.IsEnabled = false;
                }
            }
        }
    }
}
